package com.sikao.app.net;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import com.google.gson.Gson;
import com.sikao.app.app.MyApplication;
import com.sikao.app.utils.JSONUtil;
import com.sikao.app.utils.NetWorkUtil;
import com.sikao.app.utils.Utils;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.MultipartBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class OkHttpHelper {

	public static final String TAG = "OkHttpHelper";

	private static OkHttpHelper mInstance;
	private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
	private OkHttpClient mHttpClient;
	private Gson mGson;
	private Handler mHandler;

	private OkHttpHelper() {

		mHttpClient = new OkHttpClient();
		mHttpClient.setConnectTimeout(20000, TimeUnit.MILLISECONDS);
		mHttpClient.setReadTimeout(30000, TimeUnit.MILLISECONDS);
		mHttpClient.setWriteTimeout(50000, TimeUnit.MILLISECONDS);
		mGson = JSONUtil.getGson();
		mHandler = new Handler(Looper.getMainLooper());
	}

	public static OkHttpHelper getInstance() {
		if (mInstance == null) {
			synchronized (OkHttpHelper.class) {
				if (mInstance == null) {
					mInstance = new OkHttpHelper();
				}
			}
		}
		return mInstance;
	}

	/**
	 * 设置请求时间
	 *
	 * @param connectTimeout 连接时间
	 * @param readTimeout    读取时间
	 * @param writeTimeout   写入时间
	 */
	public void setOkhttpClientTime(int connectTimeout, int readTimeout, int writeTimeout) {
		mHttpClient.setConnectTimeout(connectTimeout, TimeUnit.MILLISECONDS);
		mHttpClient.setReadTimeout(readTimeout, TimeUnit.MILLISECONDS);
		mHttpClient.setWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS);
	}

	/**
	 * 重置请求时间
	 */
	public void restoreOkhttpClientTime() {
		mHttpClient.setConnectTimeout(20000, TimeUnit.MILLISECONDS);
		mHttpClient.setReadTimeout(30000, TimeUnit.MILLISECONDS);
		mHttpClient.setWriteTimeout(50000, TimeUnit.MILLISECONDS);
	}

	/**
	 * 同步的Get请求
	 *
	 * @param url
	 * @param tag 请求标志，当想取消本次请求时执行OkHttpHelper.cancelRequest(tag)即可取消本次请求，对于异步请求，callback为tag
	 * @return Response
	 */
	public Response getResponse(String url, Object tag) throws IOException {
		Request request = buildGetRequest(url, null);
		Response execute = mHttpClient.newCall(request).execute();
		return execute;
	}

	/**
	 * 异步基于get的请求
	 *
	 * @param url
	 * @param callback
	 */
	public void get(String url, BaseCallback callback) {
		Request request = buildGetRequest(url, callback);
		request(request, callback);
	}

	/**
	 * 同步基于post的请求
	 *
	 * @param url   * @param tag 请求标志，当想取消本次请求时执行OkHttpHelper.cancelRequest(tag)即可取消本次请求，对于异步请求，callback为tag
	 * @param param
	 * @return Response
	 */
	public Response postResponse(String url, Map<String, String> param, Object tag) throws IOException {
		Request request = buildPostRequest(url, param, tag);
		Response execute = mHttpClient.newCall(request).execute();
		return execute;
	}

	/**
	 * 异步基于post的请求
	 *
	 * @param url
	 * @param callback
	 * @param param
	 * @return Response
	 */
	public void post(String url, Map<String, String> param, BaseCallback callback) {
		//统一展示提交数据格式

		Utils.showLog(param.toString());
		Request request = buildPostRequest(url, param, callback);
		request(request, callback);
	}

	/**
	 * 异步下载文件
	 *
	 * @param url
	 * @param destFileDir 本地文件存储的文件夹
	 * @param fileName    文件名称（要加后缀名，比如file1.mp4,保存格式为.mp4格式）
	 * @param callback
	 */
	public void downloadFile(final String url, final String destFileDir, final String fileName, final FileDownloadCallback callback) {
		Request request = buildGetRequest(url, callback);
		Call call = mHttpClient.newCall(request);
		call.enqueue(new Callback() {
			@Override
			public void onFailure(final Request request, final IOException e) {
				sendFailedResultCallback(callback, request, e);
			}

			@Override
			public void onResponse(final Response response) {
				if (!response.isSuccessful() || fileName == null) {
					sendErrorResultCallback(callback, response, null);
					return;
				}
				long length = 0;
				long allLength;
				byte[] buf = new byte[1024];
				int len = 0;
				InputStream is = null;
				FileOutputStream fos = null;
				try {
					allLength = response.body().contentLength();
					sendAllSizeCallback(callback, allLength);
					is = response.body().byteStream();
					File file = new File(destFileDir, fileName);
					if (!file.exists()) {
						file.getParentFile().mkdirs();
					}
					fos = new FileOutputStream(file);
					while ((len = is.read(buf)) != -1) {
						fos.write(buf, 0, len);
						length = length + len;
						sendUpdateProgress(callback, length);
					}
					fos.flush();
					//如果下载文件成功，第一个参数为文件的绝对路径
					sendSuccessResultCallback(callback, file.getAbsolutePath());
				} catch (IOException e) {
					sendErrorResultCallback(callback, response, e);
				} finally {
					try {
						if (is != null) is.close();
					} catch (IOException e) {
					}
					try {
						if (fos != null) fos.close();
					} catch (IOException e) {
					}
				}

			}
		});
	}

	/**
	 * 异步基于post的文件上传
	 *
	 * @param
	 */
	public void postPictures(String url, Map<String, String> params, final BaseCallback callback) {
		MultipartBuilder builder = new MultipartBuilder().type(MultipartBuilder.FORM);
		//遍历paths中所有图片绝对路径到builder，并约定key如“upload”作为后台接受多张图片的key
		builder.addFormDataPart("obj_id", "1");
		builder.addFormDataPart("type", "mall");
		if (params != null) {
			for (Map.Entry<String, String> entry : params.entrySet()) {
				builder.addFormDataPart("photo[]", entry.getKey(), RequestBody.create(MEDIA_TYPE_PNG, new File(entry.getValue())));
			}
		}
		//构建请求体
		RequestBody requestBody = builder.build();

		//构建请求
		Request request = new Request.Builder()
				.url(url)//地址
				.post(requestBody)//添加请求体
				.addHeader("cookie", "PHPSESSID=" + HttpApi.sessionId)
				.build();
		callback.onBeforeRequest(request);
		//发送异步请求，同步会报错，Android4.0以后禁止在主线程中进行耗时操作
		mHttpClient.newCall(request).enqueue(new Callback() {
			@Override
			public void onFailure(Request request, IOException e) {
				Log.d(TAG, "request = " + request.urlString());
				Log.d(TAG, "e.getLocalizedMessage() = " + e.getLocalizedMessage());
				callback.dismissDialog();
				callbackFailure(callback, request, e);
			}

			@Override
			public void onResponse(Response response) throws IOException {
				//看清楚是response.body().string()不是response.body().toString()
				String resultStr = response.body().string();
				Log.d(TAG, "result=" + resultStr);
				Object obj = mGson.fromJson(resultStr, callback.mType);
				callback.dismissDialog();
				callbackSuccess(callback, response, obj);
			}
		});
	}

	public void cancelRequest(Object object) {
		mHttpClient.cancel(object);
	}

	public void request(final Request request, final BaseCallback callback) {
		if (!NetWorkUtil.checkNetwork(true)) {
			callbackFailure(callback, request, null);
			return;
		}
		callback.onBeforeRequest(request);
		Call call = mHttpClient.newCall(request);
		call.enqueue(new Callback() {

			@Override
			public void onFailure(Request request, IOException e) {
				callbackFailure(callback, request, e);
				callback.dismissDialog();
			}

			@Override
			public void onResponse(Response response) throws IOException {
				String session = response.headers().get("Set-Cookie");
				if (session != null) {
					Pattern p = Pattern.compile("(?<=PHPSESSID=).*?(?=;)");// 截取PHPSESSID=开头，;结尾的字符串，即获取sessionid
					Matcher m = p.matcher(session);
					if (m.find()) {
						HttpApi.sessionId = m.group(0);// 获取被匹配的部分
					}
				}

				if (response.isSuccessful()) {
					String resultStr;
					if (callback.mType == Response.class) {
						callback.dismissDialog();
						callbackResponse(callback, response);
					} else if (callback.mType == String.class) {//字符串格式
						resultStr = response.body().string();
						Log.d(TAG, "result=" + resultStr);
						callback.dismissDialog();
						callbackSuccess(callback, response, resultStr);
					} else if (callback.mType == Bitmap.class) {//图片格式
						InputStream inputStream = null;
						inputStream = response.body().byteStream();
						ByteArrayOutputStream outStream = new ByteArrayOutputStream();
						byte[] buffer = new byte[1024];
						byte[] bmp_buffer;
						int len = 0;
						while ((len = inputStream.read(buffer)) != -1) {
							outStream.write(buffer, 0, len);
						}
						outStream.flush();
						bmp_buffer = outStream.toByteArray();
						outStream.close();
						inputStream.close();
						Bitmap bitmap = BitmapFactory.decodeByteArray(bmp_buffer, 0, bmp_buffer.length);
						callbackSuccess(callback, response, bitmap);
						callback.dismissDialog();
					} else {//json解析成对象
						resultStr = response.body().string();
						Log.d(TAG, "result=" + resultStr);
						try {
							Object obj = mGson.fromJson(resultStr, callback.mType);
							callbackSuccess(callback, response, obj);
							callback.dismissDialog();
						} catch (com.google.gson.JsonParseException e) { // Json解析的错误
							callback.dismissDialog();
							callbackError(callback, response, e);
						}
					}
				} else {
					callbackError(callback, response, null);
				}

			}
		});
	}


	private void callbackSuccess(final BaseCallback callback, final Response response, final Object obj) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				callback.onSuccess(response, obj);
			}
		});
	}


	private void callbackError(final BaseCallback callback, final Response response, final Exception e) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				callback.onError(response, response.code(), e);
				if (e != null) {
					Utils.showLog("数据解析异常：" + e.toString());
					Toast.makeText(MyApplication.getInstance(), "数据解析异常", Toast.LENGTH_SHORT).show();
				} else {
					Toast.makeText(MyApplication.getInstance(), "服务器异常" + response.code(), Toast.LENGTH_SHORT).show();
				}
			}
		});
	}


	private void callbackFailure(final BaseCallback callback, final Request request, final IOException e) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				if (e == null) {
					callback.onFailure(null, null);
				} else if (e.getMessage() != null && !e.getMessage().equals("Socket closed")) {
					if (e.getMessage().contains("after")) {
						callback.onFailure(request, e);
						Toast.makeText(MyApplication.getInstance(), "连接超时,请检查网络", Toast.LENGTH_SHORT).show();
					} else {
						callback.onFailure(request, null);
						Toast.makeText(MyApplication.getInstance(), "找不到服务器", Toast.LENGTH_SHORT).show();
					}
				}

			}
		});
	}


	private void callbackResponse(final BaseCallback callback, final Response response) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				callback.onResponse(response);
			}
		});
	}

	private void sendSuccessResultCallback(final FileDownloadCallback callback, final String filePath) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				callback.onSuccess(filePath);
			}
		});
	}

	private void sendAllSizeCallback(final FileDownloadCallback callback, final long length) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				callback.onAllSize(length);
			}
		});
	}

	private void sendFailedResultCallback(final FileDownloadCallback callback, final Request request, final Exception e) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				callback.onFailure(request, e);
			}
		});
	}

	private void sendErrorResultCallback(final FileDownloadCallback callback, final Response response, final Exception e) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				callback.onError(response, response.code(), e);
			}
		});
	}

	private void sendUpdateProgress(final FileDownloadCallback callback, final long length) {
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				callback.onUpdateProgress(length);
			}
		});
	}

	private Request buildPostRequest(String url, Map<String, String> params, Object object) {
		return buildRequest(url, HttpMethodType.POST, params, object);
	}

	private Request buildGetRequest(String url, Object object) {
		return buildRequest(url, HttpMethodType.GET, null, object);
	}

	private Request buildRequest(String url, HttpMethodType methodType, Map<String, String> params, Object object) {

		Request.Builder builder = new Request.Builder()
				.url(url)
				.header("User-Agent", "OkHttp Headers.java");
		if (HttpApi.sessionId != null) {
			builder.addHeader("cookie", "PHPSESSID=" + HttpApi.sessionId);
		}
		//设置tag，当取消请求时用，取消请求时执行mHttpClient.cancel(tag);
		if (object != null) {
			builder.tag(object);
		}
		if (methodType == HttpMethodType.POST) {
			RequestBody body = builderFormData(params);
			builder.post(body);
		} else if (methodType == HttpMethodType.GET) {
			builder.get();
		}
		return builder.build();
	}


	private RequestBody builderFormData(Map<String, String> params) {

		FormEncodingBuilder builder = new FormEncodingBuilder();
		if (params != null) {
			for (Map.Entry<String, String> entry : params.entrySet()) {
				builder.add(entry.getKey(), entry.getValue());
			}
		}
		return builder.build();
	}

	enum HttpMethodType {
		GET,
		POST,
	}

}
